package com.siping.算法;

import java.io.*;
import java.util.HashMap;
import java.util.PriorityQueue;

/**
 * @author Yang Xu
 * @date 2021/9/6 17:30
 * @description:
 */
public class TopK {

    public static final String WRITE_FILE = "D:\\tmp\\test\\data\\words3.txt";

    private static final Integer TOP_K = 10;

    public static void main(String[] args) throws IOException {
        // 1.考虑到资源受限，先将大文件拆分为多个小文件，按照hash取模

        // 2.通过最小堆统计topK，PriorityQueue就可以实现最小堆
        //    2.1 先把所有数据读入内存
        long start = System.currentTimeMillis();
        HashMap<String, Integer> dataMap = new HashMap<>();
        InputStream is = new FileInputStream(WRITE_FILE);
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        String line = reader.readLine();
        while (line != null) {
            dataMap.put(line, dataMap.getOrDefault(line, 0) + 1);
            line = reader.readLine();
        }
        is.close();
        reader.close();
        long time2 = System.currentTimeMillis() - start;
        System.out.println("读取数据完毕，耗时：" + time2 + "ms");
        //    2.2 最小堆求topK
        PriorityQueue<Node> minHeap = new PriorityQueue<>(10);
        dataMap.forEach((k, v) -> {
            Node node = new Node(k, v);
            if (minHeap.size() < TOP_K) {
                minHeap.add(node);
            } else {
                if (minHeap.peek().compareTo(node) < 0) {
                    minHeap.poll();
                    minHeap.add(node);
                }
            }
        });
        System.out.println("=====================================统计成功，耗时：" + (System.currentTimeMillis() - start + time2) + "ms");
        // 打印结果
        minHeap.forEach(item -> System.out.println(item.word + " : " + item.count));
    }

    static class Node implements Comparable<Node> {
        String word;
        int count;

        public Node(String word, int count) {
            this.word = word;
            this.count = count;
        }

        @Override
        public int compareTo(Node node) {
            //正常是通过Node对象里的count来比较大小的
            if (this.count > node.count) {
                return 1;
            } else if (this.count < node.count) {
                return -1;
            } else {
                //比如["2","2","1","1"]的情况 数组中字符串2出现2次 字符串1出现2次 这时候要求按字典顺序输出["1","2"]、["2","2"]1出现2次 2出现2次
                //此时使用原生的比较器 用2个Node对象的string进行字典顺序比较
                //上面的2个条件比较对象是堆顶的元素 被比较对象是要是否加入替代堆顶最小元素的元素 用的是count大小做比较
                //但此时Node值相同,应该比较的是要加入的node的name字典值,小于栈顶元素 才会重新进行构建最小堆 应该反过来比较
                return node.word.compareTo(this.word);
            }
        }
    }
}
